// License Notification
//======================================================
// Copyright (c) 2010 Channel Intelligence Inc.
// All rights reserved. This program and the accompanying materials
// are made available under the terms of the Eclipse Public License v1.0
// which accompanies this distribution, and is available at
// http://www.eclipse.org/legal/epl-v10.html
//
// ======================================================




using System;
using System.IO;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.Serialization;
using System.Runtime.Serialization.Formatters.Binary;
using System.Text;
using System.Threading;
using CIP4.Common;
using CIP4.Item;

namespace CIP4.DataProvider.Dictionary
{
    class ItemDictionariesType
    {
        public string CollectionsDirectory { get; private set; }

        // These dictionaries can only be accessed inside a lock acquired by construction of a CollectionLockType
        // object so there should not be any contention with other threads over access to the dictionaries

        private Dictionary<string, Dictionary<string, string>> Collections { get; set; }

        private Dictionary<string, DateTime> CollectionTimestamps { get; set; }

        private Dictionary<string, bool> DirtyCollections { get; set; }

        public ItemDictionariesType(string collectionsDirectory)
        {
            Collections = new Dictionary<string, Dictionary<string, string>>();
            DirtyCollections = new Dictionary<string, bool>();
            CollectionTimestamps = new Dictionary<string, DateTime>();
            CollectionsDirectory = collectionsDirectory;
        }

        public bool ContainsCollection(string collectionID)
        {
            return Collections.ContainsKey(collectionID);
        }

        public bool RemoveCollection(string collectionID)
        {
            bool result = false;

            if (Collections.ContainsKey(collectionID))
            {
                Collections.Remove(collectionID);
                DirtyCollections.Remove(collectionID);
                CollectionTimestamps.Remove(collectionID);
                result = true;
            }
            string dictionaryFileName = Path.Combine(CollectionsDirectory, collectionID + ".data");

            FileInfo dictionaryFile = new FileInfo(dictionaryFileName);

            if (dictionaryFile.Exists)
            {
                try
                {
                    dictionaryFile.Delete();
                    return true;
                }
                catch(FileNotFoundException)
                {/* seems a trifle unlikely... */ }
            }
            return result;
        }

        public void CleanCollection(string collectionID)
        {
            DirtyCollections.Remove(collectionID);
        }

        public void DirtyCollection(string collectionID)
        {
            DirtyCollections[collectionID] = true;
        }

        public bool IsCollectionDirty(string collectionID)
        {
            return DirtyCollections.ContainsKey(collectionID);
        }

        public Dictionary<string, string> GetCollection(string collectionID, bool temporary)
        {
            string dictionaryFileName = Path.Combine(CollectionsDirectory, collectionID + ".data");

            FileInfo dictionaryFile = new FileInfo(dictionaryFileName);

            Dictionary<string, string> Items = null;

            if (Collections.ContainsKey(collectionID) && (temporary || CollectionTimestamps[collectionID] == dictionaryFile.LastWriteTimeUtc))
                return Collections[collectionID];

            Items = new Dictionary<string, string>();

            if (!temporary)
            {
                if (Collections.ContainsKey(collectionID))
                    RemoveCollection(collectionID);

                if (!dictionaryFile.Exists)
                {
                    Items = new Dictionary<string, string>(StringComparer.CurrentCultureIgnoreCase);
                }
                else
                {
                    Stream stream = null;
                    try
                    {
                        IFormatter formatter = new BinaryFormatter();
                        stream = new FileStream(dictionaryFileName, FileMode.Open, FileAccess.Read, FileShare.Read);
                        Items = (Dictionary<string, string>)formatter.Deserialize(stream);
                    }
                    catch
                    {
                        Items = new Dictionary<string, string>(StringComparer.CurrentCultureIgnoreCase);
                    }
                    finally
                    {
                        if (stream != null)
                            stream.Close();
                    }
                }
            }
            CollectionTimestamps.Add(collectionID, dictionaryFile.LastWriteTimeUtc);
            DirtyCollections.Add(collectionID, false);
            Collections.Add(collectionID, Items);
            return Items;
        }

        public void SaveCollection(string collectionID)
        {
            lock (Collections)
            {
                if (Collections.ContainsKey(collectionID))
                {
                    Stream stream = null;
                    try
                    {
                        Dictionary<string, string> Items = Collections[collectionID];
                        IFormatter formatter = new BinaryFormatter();
                        stream = new FileStream(Path.Combine(CollectionsDirectory, collectionID + ".data"), FileMode.Create, FileAccess.Write, FileShare.None);
                        formatter.Serialize(stream, Items);
                    }
                    finally
                    {
                        if (stream != null)
                        {
                            stream.Close();
                            CollectionTimestamps[collectionID] = new FileInfo(Path.Combine(CollectionsDirectory, collectionID + ".data")).LastWriteTimeUtc;
                        }
                    }
                }
            }
        }
    }
}
